Coverage Report

Created: 2026-02-05 09:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\resolver\mod.rs
Line
Count
Source
1
pub(crate) mod stub;
2
3
use crate::dns::packet::additional::AdditionalSection;
4
use crate::dns::packet::answer::AnswerSection;
5
use crate::dns::packet::authority::AuthoritySection;
6
use crate::dns::packet::question::QuestionSection;
7
use crate::exceptions::SCloudException;
8
9
/// Check that each question has at least one corresponding record
10
/// in the answer, authority, or additional sections.
11
///
12
/// This prevents responses that do not actually answer the query.
13
///
14
/// # Exemple :
15
/// ```
16
/// use crate::dns::resolver::check_answer_diff;
17
/// use crate::dns::packet::question::QuestionSection;
18
/// use crate::dns::packet::answer::AnswerSection;
19
/// use crate::dns::q_type::DNSRecordType;
20
/// use crate::dns::q_class::DNSClass;
21
///
22
/// let questions = vec![QuestionSection {
23
///     q_name: "example.com".to_string(),
24
///     q_type: DNSRecordType::A,
25
///     q_class: DNSClass::IN,
26
/// }];
27
///
28
/// let answers = vec![AnswerSection {
29
///     q_name: "example.com".to_string(),
30
///     r_type: DNSRecordType::A,
31
///     r_class: DNSClass::IN,
32
///     ttl: 300,
33
///     rdlength: 4,
34
///     rdata: vec![93, 184, 216, 34],
35
/// }];
36
///
37
/// assert!(check_answer_diff(&questions, &answers, &[], &[]).is_ok());
38
/// ```
39
1
pub(crate) fn check_answer_diff(
40
1
    questions: &[QuestionSection],
41
1
    answers: &[AnswerSection],
42
1
    authorities: &[AuthoritySection],
43
1
    additionals: &[AdditionalSection],
44
1
) -> Result<(), SCloudException> {
45
1
    for q in questions {
46
1
        let found_in_answers = answers
47
1
            .iter()
48
1
            .any(|a| 
a.q_name == q.q_name0
&&
a.r_class == q.q_class0
);
49
1
        let found_in_authorities = authorities
50
1
            .iter()
51
1
            .any(|a| a.q_name == q.q_name && a.q_class == q.q_class);
52
1
        let found_in_additionals = additionals
53
1
            .iter()
54
1
            .any(|a| 
a.q_name == q.q_name0
&&
a.q_class == q.q_class0
);
55
56
1
        if !found_in_answers && !found_in_authorities && 
!found_in_additionals0
{
57
0
            return Err(SCloudException::SCLOUD_RESOLVER_ANSWER_MISMATCH);
58
1
        }
59
    }
60
61
1
    Ok(())
62
1
}
63
64
/// Ensure that authority records belong to the same zone
65
/// as the original DNS questions.
66
///
67
/// This prevents out-of-zone NS records.
68
///
69
/// # Exemple :
70
/// ```
71
/// use crate::dns::resolver::check_authority_diff;
72
/// use crate::dns::packet::question::QuestionSection;
73
/// use crate::dns::packet::authority::AuthoritySection;
74
/// use crate::dns::q_type::DNSRecordType;
75
/// use crate::dns::q_class::DNSClass;
76
///
77
/// let questions = vec![QuestionSection {
78
///     q_name: "example.com".to_string(),
79
///     q_type: DNSRecordType::NS,
80
///     q_class: DNSClass::IN,
81
/// }];
82
///
83
/// let authorities = vec![AuthoritySection {
84
///     q_name: "example.com".to_string(),
85
///     q_type: DNSRecordType::NS,
86
///     q_class: DNSClass::IN,
87
///     ttl: 3600,
88
///     ns_name: "ns1.example.com".to_string(),
89
/// }];
90
///
91
/// assert!(check_authority_diff(&questions, &authorities).is_ok());
92
/// ```
93
#[allow(unused)]
94
0
pub(crate) fn check_authority_diff(
95
0
    questions: &[QuestionSection],
96
0
    authorities: &[AuthoritySection],
97
0
) -> Result<(), SCloudException> {
98
0
    for record in authorities.iter() {
99
0
        if !questions.iter().any(|q| record.q_name == q.q_name) {
100
0
            return Err(SCloudException::SCLOUD_RESOLVER_RECORD_OUT_OF_ZONE);
101
0
        }
102
    }
103
0
    Ok(())
104
0
}
105
106
/// Ensure that additional records correspond to the original questions.
107
///
108
/// This is commonly used to validate glue records.
109
///
110
/// # Exemple :
111
/// ```
112
/// use crate::dns::resolver::check_additional_diff;
113
/// use crate::dns::packet::question::QuestionSection;
114
/// use crate::dns::packet::additional::AdditionalSection;
115
/// use crate::dns::q_type::DNSRecordType;
116
/// use crate::dns::q_class::DNSClass;
117
///
118
/// let questions = vec![QuestionSection {
119
///     q_name: "example.com".to_string(),
120
///     q_type: DNSRecordType::A,
121
///     q_class: DNSClass::IN,
122
/// }];
123
///
124
/// let additionals = vec![AdditionalSection {
125
///     q_name: "example.com".to_string(),
126
///     q_type: DNSRecordType::A,
127
///     q_class: DNSClass::IN,
128
///     ttl: 300,
129
///     rdlength: 4,
130
///     rdata: vec![192, 0, 2, 1],
131
/// }];
132
///
133
/// assert!(check_additional_diff(&questions, &additionals).is_ok());
134
/// ```
135
#[allow(unused)]
136
0
pub(crate) fn check_additional_diff(
137
0
    questions: &[QuestionSection],
138
0
    additionals: &[AdditionalSection],
139
0
) -> Result<(), SCloudException> {
140
0
    for record in additionals.iter() {
141
0
        if !questions.iter().any(|q| record.q_name == q.q_name) {
142
0
            return Err(SCloudException::SCLOUD_RESOLVER_RECORD_OUT_OF_ZONE);
143
0
        }
144
    }
145
0
    Ok(())
146
0
}